library(readxl)
mydata <- read_xlsx("./Diabetes.xlsx")
mydata <- as.data.frame(mydata)
head(mydata)
##   ID Pregnancies Glucose Pressure  BMI Age Diabetes
## 1  1           1      97       64 18.2  21      neg
## 2  2           1      83       68 18.2  27      neg
## 3  3           1      97       70 18.2  21      neg
## 4  4           0     104       76 18.4  27      neg
## 5  5           1      80       55 19.1  21      neg
## 6  6           3      99       80 19.3  30      neg

Description of variables used:

mydata$Diabetes <- factor(mydata$Diabetes, 
                          levels = c("neg", "pos"), 
                          labels = c("neg", "pos"))
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
mydata %>%
  select(Diabetes, Pregnancies, Glucose, Pressure, BMI, Age) %>%
  summary()
##  Diabetes   Pregnancies       Glucose         Pressure    
##  neg:422   Min.   :0.000   Min.   : 44.0   Min.   : 24.0  
##  pos:184   1st Qu.:1.000   1st Qu.: 99.0   1st Qu.: 64.0  
##            Median :2.000   Median :115.0   Median : 70.0  
##            Mean   :2.713   Mean   :120.7   Mean   : 71.4  
##            3rd Qu.:4.000   3rd Qu.:139.0   3rd Qu.: 80.0  
##            Max.   :7.000   Max.   :199.0   Max.   :122.0  
##       BMI             Age       
##  Min.   :18.20   Min.   :21.00  
##  1st Qu.:27.40   1st Qu.:23.00  
##  Median :32.00   Median :27.00  
##  Mean   :32.39   Mean   :31.12  
##  3rd Qu.:36.58   3rd Qu.:36.00  
##  Max.   :67.10   Max.   :70.00
fit0 <- glm(Diabetes ~ 1, #Dependent and explanatory variables
            family = binomial, #Binary logistic regression
            data = mydata)

summary(fit0)
## 
## Call:
## glm(formula = Diabetes ~ 1, family = binomial, data = mydata)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -0.83007    0.08834  -9.396   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 744.06  on 605  degrees of freedom
## Residual deviance: 744.06  on 605  degrees of freedom
## AIC: 746.06
## 
## Number of Fisher Scoring iterations: 4
exp(cbind(odds = fit0$coefficients, confint.default(fit0))) #Odds for Y=1
##                 odds     2.5 %    97.5 %
## (Intercept) 0.436019 0.3666974 0.5184452
head(fitted(fit0)) #Estimated probability for Y=1
##         1         2         3         4         5         6 
## 0.3036304 0.3036304 0.3036304 0.3036304 0.3036304 0.3036304
ACC <- 422/606 #Prop. of correctly classified units

ACC
## [1] 0.6963696
fit1 <- glm(Diabetes ~ Pregnancies + Glucose + Pressure + BMI + Age,  
            family = binomial, 
            data = mydata)
library(car)
## Loading required package: carData
## 
## Attaching package: 'car'
## The following object is masked from 'package:dplyr':
## 
##     recode
vif(fit1)
## Pregnancies     Glucose    Pressure         BMI         Age 
##    1.251598    1.044367    1.202577    1.136284    1.370504
mydata$StdResid <- rstandard(fit1)

hist(mydata$StdResid,
     main = "Histogram of standardised residuals",
     ylab = "Frequency",
     xlab = "Standardised residuals")

library(dplyr)
# Calculate Cook's distance
mydata$CooksD <- cooks.distance(fit1)

# Define Cook's distance threshold
cd_threshold <- 4/nrow(mydata)

# Display apartments with Cook's distance above the threshold
influential_units <- mydata %>%
  filter(CooksD > cd_threshold) %>%
  arrange(desc(CooksD)) %>%
  select(ID, Diabetes, Pregnancies, Glucose, Pressure, BMI, Age, CooksD)

head(influential_units, 6) %>%
  mutate(CooksD = round(CooksD, 3))
##    ID Diabetes Pregnancies Glucose Pressure  BMI Age CooksD
## 1 590      neg           0     173       78 46.5  58  0.031
## 2 173      pos           0     105       84 27.9  62  0.023
## 3  30      neg           0     161       50 21.9  65  0.021
## 4 600      neg           0     165       90 52.3  23  0.016
## 5 382      neg           7     179       95 34.2  60  0.016
## 6 603      pos           1      88       30 55.0  26  0.016
fit1 <- glm(Diabetes ~ Pregnancies + Glucose + Pressure + BMI + Age,  
            family = binomial, 
            data = mydata)
anova(fit0, fit1, test = "Chisq") #Comparision of models based on -2LL statistics
## Analysis of Deviance Table
## 
## Model 1: Diabetes ~ 1
## Model 2: Diabetes ~ Pregnancies + Glucose + Pressure + BMI + Age
##   Resid. Df Resid. Dev Df Deviance  Pr(>Chi)    
## 1       605     744.06                          
## 2       600     551.24  5   192.82 < 2.2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
logLik_fit0 <- as.numeric(logLik(fit0))
logLik_fit1 <- as.numeric(logLik(fit1))

McFadden_R2 <- 1 - (logLik_fit1 / logLik_fit0)
McFadden_R2
## [1] 0.2591475
summary(fit1)
## 
## Call:
## glm(formula = Diabetes ~ Pregnancies + Glucose + Pressure + BMI + 
##     Age, family = binomial, data = mydata)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -8.580862   0.870336  -9.859  < 2e-16 ***
## Pregnancies  0.066884   0.053407   1.252  0.21045    
## Glucose      0.034899   0.003975   8.779  < 2e-16 ***
## Pressure    -0.013465   0.009253  -1.455  0.14560    
## BMI          0.094391   0.017034   5.541    3e-08 ***
## Age          0.031009   0.010846   2.859  0.00425 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 744.06  on 605  degrees of freedom
## Residual deviance: 551.24  on 600  degrees of freedom
## AIC: 563.24
## 
## Number of Fisher Scoring iterations: 5
exp(cbind(OR = fit1$coefficients, confint.default(fit1))) #Odds ratio for Y=1 (with 95% CI)
##                       OR        2.5 %      97.5 %
## (Intercept) 0.0001876632 3.408375e-05 0.001033263
## Pregnancies 1.0691712041 9.629124e-01 1.187155800
## Glucose     1.0355147782 1.027478e+00 1.043614584
## Pressure    0.9866249190 9.688935e-01 1.004680871
## BMI         1.0989891661 1.062905e+00 1.136298837
## Age         1.0314952587 1.009800e+00 1.053656253
mydata$EstProb <- fitted(fit1) #Estimates of probabilities for Y=1: P(Y=1)
head(mydata[c("ID", "Diabetes", "EstProb")])
##   ID Diabetes    EstProb
## 1  1      neg 0.02604660
## 2  2      neg 0.01838142
## 3  3      neg 0.02407367
## 4  4      neg 0.03227302
## 5  5      neg 0.01783469
## 6  6      neg 0.03731102
mydata$Classification <- ifelse(test = mydata$EstProb < 0.50, 
                                yes = "NO", 
                                no = "YES")

#If estimated probability is below 0.50, person is classified into a group of people without problems of loan repayment (NO), otherwise YES.

mydata$Classification <- factor(mydata$Classification,
                                levels = c("NO", "YES"), 
                                labels = c("NO", "YES"))
Table <- table(
  Actual    = mydata$Diabetes,
  Predicted = mydata$Classification
)
Table
##       Predicted
## Actual  NO YES
##    neg 382  40
##    pos  89  95
# Install once if not already installed
# install.packages("pROC")

library(pROC)
## Type 'citation("pROC")' for a citation.
## 
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
# Loads the pROC package, which provides functions to compute and plot
# Receiver Operating Characteristic (ROC) curves and to calculate AUC.

# Compute ROC curve
roc_fit1 <- roc(response = mydata$Diabetes,
                predictor = mydata$EstProb,
                levels = c("neg", "pos"),
                direction = "<")
# Constructs the ROC curve object for model fit1.
# - response: the observed outcome (diabetes status),
# - predictor: predicted probabilities P(Y = 1) from the logistic model,
# - levels: specifies which category is treated as non-event ("neg")
#   and which as event ("pos"),
# - direction: indicates that higher predicted probabilities correspond
#   to a higher likelihood of the event (diabetes).
# Area Under the Curve (AUC)
auc_fit1 <- auc(roc_fit1)
auc_fit1
## Area under the curve: 0.8336
# Computes the area under the ROC curve.
# The AUC summarises the model’s overall discriminatory ability
# independently of any particular classification threshold.

# Plotting ROC curve, including AUC
plot(roc_fit1,
     col = "blue",
     lwd = 2,
     main = paste("ROC Curve for Model fit1 (AUC =",
                  round(auc_fit1, 3), ")"))
# Plots the ROC curve.
# - The y-axis represents sensitivity (true positive rate),
# - The x-axis represents 1 - specificity (false positive rate),
# - The AUC value is displayed in the plot title for interpretation.

# Add reference line for random classification
abline(a = 0, b = 1, lty = 2, col = "gray")

# Adds the 45-degree reference line corresponding to random classification.
# A model with no discriminatory power would lie close to this line.
coords(roc_fit1,
       x = "best",
       best.method = "youden",
       ret = c("threshold", "sensitivity", "specificity", "accuracy"))
##           threshold sensitivity specificity  accuracy
## threshold 0.2382797   0.8152174   0.7014218 0.7359736